﻿using System.Security.Cryptography;
using SystemX509 = System.Security.Cryptography.X509Certificates;

using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Security;

namespace Microsoft.Cryptography.BouncyCastle
{
    /// <summary>
    /// A class containing methods to interface the BouncyCastle world to the .NET Crypto world.
    /// </summary>
    public sealed class BouncyCastleUtilities
    {
        private BouncyCastleUtilities()
        {
        }

        /// <summary>
        /// Create an System.Security.Cryptography.X509Certificate from an X509Certificate Structure.
        /// </summary>
        /// <param name="x509Struct"></param>
        /// <returns>A System.Security.Cryptography.X509Certificate.</returns>
        public static SystemX509.X509Certificate ToX509Certificate(
            X509CertificateStructure x509Struct)
        {
            return new SystemX509.X509Certificate(x509Struct.GetDerEncoded());
        }

        /// <summary>
        /// Static method to convert a Bouncycaste X509-Certificate to the corresponding .Net class
        /// </summary>
        /// <param name="x509Cert">Bouncycastle x.409 certificate</param>
        /// <returns>.Net X509Certificate</returns>
        public static SystemX509.X509Certificate ToX509Certificate(
            X509Certificate x509Cert)
        {
            return new SystemX509.X509Certificate(x509Cert.GetEncoded());
        }

        /// <summary>
        /// Static method to convert a .Net X509-Certificate to the corresponding Bouncycaste class
        /// </summary>
        /// <param name="x509Cert">.Net X.509 Certificate</param>
        /// <returns>Bouncycaste X.509 certificate</returns>
        public static X509Certificate FromX509Certificate(
            SystemX509.X509Certificate x509Cert)
        {
            return new X509CertificateParser().ReadCertificate(x509Cert.GetRawCertData());
        }

        /// <summary>
        /// Returns the DsaKeypair of a DSACryptoServiceProvider
        /// </summary>
        /// <param name="dsaCsp">DSA CryptoServiceProvider</param>
        /// <returns>DsaKeyPair</returns>
        public static AsymmetricCipherKeyPair GetDsaKeyPair(
            DSACryptoServiceProvider dsaCsp)
        {
            return GetDsaKeyPair(dsaCsp.ExportParameters(true));
        }

        /// <summary>
        /// Returns the DsaKeypair of a DSACryptoServiceProvider
        /// </summary>
        /// <param name="dp">DSA Keypair</param>
        /// <returns>DsaKeypair</returns>
        public static AsymmetricCipherKeyPair GetDsaKeyPair(
            DSAParameters dp)
        {
            DsaValidationParameters validationParameters = (dp.Seed != null)
                ? new DsaValidationParameters(dp.Seed, dp.Counter)
                : null;

            DsaParameters parameters = new DsaParameters(
                new BigInteger(1, dp.P),
                new BigInteger(1, dp.Q),
                new BigInteger(1, dp.G),
                validationParameters);

            DsaPublicKeyParameters pubKey = new DsaPublicKeyParameters(
                new BigInteger(1, dp.Y),
                parameters);

            DsaPrivateKeyParameters privKey = new DsaPrivateKeyParameters(
                new BigInteger(1, dp.X),
                parameters);

            return new AsymmetricCipherKeyPair(pubKey, privKey);
        }

        /// <summary>
        /// Returns the DSA public key of a DSACryptoServiceProvider
        /// </summary>
        /// <param name="dsaCsp">DSA CryptoServiceProvider</param>
        /// <returns>DSA Public Key parameters</returns>
        public static DsaPublicKeyParameters GetDsaPublicKey(
            DSACryptoServiceProvider dsaCsp)
        {
            return GetDsaPublicKey(dsaCsp.ExportParameters(false));
        }

        /// <summary>
        /// Returns the DSA public key of a DSACryptoServiceProvider
        /// </summary>
        /// <param name="dp">DSA Keypair</param>
        /// <returns>DSA Public Key parameters</returns>
        public static DsaPublicKeyParameters GetDsaPublicKey(
            DSAParameters dp)
        {
            DsaValidationParameters validationParameters = (dp.Seed != null)
                ? new DsaValidationParameters(dp.Seed, dp.Counter)
                : null;

            DsaParameters parameters = new DsaParameters(
                new BigInteger(1, dp.P),
                new BigInteger(1, dp.Q),
                new BigInteger(1, dp.G),
                validationParameters);

            return new DsaPublicKeyParameters(
                new BigInteger(1, dp.Y),
                parameters);
        }

        /// <summary>
        /// Returns the RSA Key pair for a RSA CryptoServiceProvider
        /// </summary>
        /// <param name="rsaCsp">RSA CryptoServiceProvider</param>
        /// <returns>RSA key pair</returns>
        public static AsymmetricCipherKeyPair GetRsaKeyPair(
            RSACryptoServiceProvider rsaCsp)
        {
            return GetRsaKeyPair(rsaCsp.ExportParameters(true));
        }

        /// <summary>
        /// Returns the RSA Key pair for a RSA CryptoServiceProvider
        /// </summary>
        /// <param name="rp">RSA parameters</param>
        /// <returns>RSA key pair</returns>
        public static AsymmetricCipherKeyPair GetRsaKeyPair(
            RSAParameters rp)
        {
            BigInteger modulus = new BigInteger(1, rp.Modulus);
            BigInteger pubExp = new BigInteger(1, rp.Exponent);

            RsaKeyParameters pubKey = new RsaKeyParameters(
                false,
                modulus,
                pubExp);

            RsaPrivateCrtKeyParameters privKey = new RsaPrivateCrtKeyParameters(
                modulus,
                pubExp,
                new BigInteger(1, rp.D),
                new BigInteger(1, rp.P),
                new BigInteger(1, rp.Q),
                new BigInteger(1, rp.DP),
                new BigInteger(1, rp.DQ),
                new BigInteger(1, rp.InverseQ));

            return new AsymmetricCipherKeyPair(pubKey, privKey);
        }

        /// <summary>
        /// Returns the RSA public key of a RSACryptoServiceProvider
        /// </summary>
        /// <param name="rsaCsp">RSA CryptoServiceProvider</param>
        /// <returns>RSA Public Key parameters</returns>
        public static RsaKeyParameters GetRsaPublicKey(
            RSACryptoServiceProvider rsaCsp)
        {
            return GetRsaPublicKey(rsaCsp.ExportParameters(false));
        }

        /// <summary>
        /// Returns the RSA public key of RSAParameters
        /// </summary>
        /// <param name="rp">RSA parameters</param>
        /// <returns>RSA Public Key parameters</returns>
        public static RsaKeyParameters GetRsaPublicKey(
            RSAParameters rp)
        {
            return new RsaKeyParameters(
                false,
                new BigInteger(1, rp.Modulus),
                new BigInteger(1, rp.Exponent));
        }

        /// <summary>
        /// Returns the DigesIdentifier for a given hash Algorithm.
        /// </summary>
        /// <param name="hashAlgorithm">hash algorithm</param>
        /// <returns>DigestIdentifier for the hash algorithm</returns>
        public static string DigestIdentifierFroHashAlgorithm(HashAlgorithm hashAlgorithm)
        {
            if (hashAlgorithm is System.Security.Cryptography.SHA1)
            {
                return "SHA1";
            }
            else if (hashAlgorithm is System.Security.Cryptography.SHA256)
            {
                return "SHA256";
            }
            else if (hashAlgorithm is System.Security.Cryptography.SHA384)
            {
                return "SHA384";
            }
            else if (hashAlgorithm is System.Security.Cryptography.SHA512)
            {
                return "SHA512";
            }
            else if (hashAlgorithm is System.Security.Cryptography.RIPEMD160)
            {
                return "RIPEMD-160";
            }
            else if (hashAlgorithm is System.Security.Cryptography.MD5)
            {
                return "1.2.840.113549.2.5";
            }
            else
            {
                throw new System.Exception(string.Format("Could not map hash algorithm '{0}' to a BouncyCastle DigestInfo.",
                    hashAlgorithm.GetType().ToString()));
            }
        }
    }
}
